SPB Git
2.8 KB · 34 lines typescript
1import { z } from "zod";2import { handler, parseBody, uuid, HttpError } from "@/lib/api";3import { createRow, updateRow, deleteRows, reorderRows, listRowsSimple, getDatabase, queryRows } from "@/lib/databases/service";4import { audit } from "@/lib/audit";5const values = z.record(z.string(), z.unknown());6/** GET ?q=&limit= → lightweight refs (relation pickers); ?full=1 → hydrated rows. */7export const GET = handler(async ({ ws, params, req }) => {8  const u = new URL(req.url); const q = u.searchParams.get("q") ?? undefined; const limit = Math.min(500, Number(u.searchParams.get("limit") ?? 50));9  if (u.searchParams.get("full") === "1") { const d = await getDatabase(ws.id, params.id); return Response.json(await queryRows(d.id, d.properties, { search: q, limit, offset: Number(u.searchParams.get("offset") ?? 0) })); }10  return Response.json(await listRowsSimple(ws.id, params.id, q, limit));11});12/** { title?, values?, position?, content? } or { rows: [...] } */13export const POST = handler(async ({ ws, params, req, user }) => {14  const row = z.object({ title: z.string().max(500).optional(), values: values.optional(), position: z.number().optional(), content: z.any().optional(), icon: z.string().max(16).nullable().optional() });15  const b = await parseBody(req, z.union([z.object({ rows: z.array(row).max(500) }), row.strict()]));16  const inputs = "rows" in b ? b.rows : [b]; const out = []; for (const i of inputs) out.push(await createRow(ws.id, params.id, i));17  await audit(user.id, "database.row.create", { type: "database", id: params.id }, { count: out.length });18  return Response.json("rows" in b ? out : out[0], { status: 201 });19});20/** { id, values } | { updates: [{ id, values }] } | { order: [ids] } */21export const PATCH = handler(async ({ ws, params, req }) => {22  const b = await parseBody(req, z.object({ id: uuid.optional(), values: values.optional(), updates: z.array(z.object({ id: uuid, values })).max(500).optional(), order: z.array(uuid).max(5000).optional() }));23  if (b.order) { await reorderRows(ws.id, params.id, b.order); return Response.json({ ok: true }); }24  if (b.updates) { const out = []; for (const u of b.updates) out.push(await updateRow(ws.id, params.id, u.id, u.values)); return Response.json(out); }25  if (!b.id) throw new HttpError(400, "id required");26  return Response.json(await updateRow(ws.id, params.id, b.id, b.values ?? {}));27});28/** ?id=… or ?ids=a,b,c */29export const DELETE = handler(async ({ ws, params, req, user }) => {30  const u = new URL(req.url); const ids = [...(u.searchParams.get("ids")?.split(",") ?? []), ...(u.searchParams.get("id") ? [u.searchParams.get("id")!] : [])].map((x) => uuid.parse(x));31  const n = await deleteRows(ws.id, params.id, ids); await audit(user.id, "database.row.delete", { type: "database", id: params.id }, { count: n });32  return Response.json({ ok: true, deleted: n });33});34
Files · main
Menu

Theme